#!/usr/bin/perl -T

# (C) 2008 The Trustees of Indiana University.  All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
#

#there are three commands:
# 1. Read network status
# 2. Read network descriptors
# 3. Upload server descriptor

#what is stored in DB
#network status



use strict;
use 5.004;

use CGI qw/:standard/;

use DBI;
use DBI qw(:sql_types);
use DBD::mysql;
use Socket;
use Digest::MD5 qw(md5_hex);
use List::Util qw[min max];
use File::Temp qw/ tempfile tempdir /;
use XML::Simple qw/XMLin /;
use Data::Dumper;
use Digest::HMAC_MD5 qw(hmac_md5 hmac_md5_hex);
use Digest::SHA1  qw(sha1_hex);
use Digest::HMAC_SHA1 qw(hmac_sha1 hmac_sha1_hex);
#use Switch;

#use a global for the dbhandler..
my $dbh;
my %config;

#make sure the file is OUTSIDE the web server scope
my $config_filename="/var/www/tdor-ds.conf";


#----------------------------------
#-----now the functions

sub load_config{
   open(CONFIG,"< $config_filename") or die;
   while (<CONFIG>){
      if($_ =~ /^#/){
         next;
      }
     if ($_ =~ /^\s*(\w+)\s*=\s*(\w+)/){
        #print "$1 $2 \n";
        $config{$1}=$2;
     } 
   }
   close(CONFIG);
}

sub upload_server_descriptor{
  #$server_key
  my $version=shift;
  my $public_desc=shift;
  my $ip_addr;
  my $port;
  my $pub_key;
  my $is_client;
  my $is_exit;
  my $kbps; 
  my $query;
  my $temp;
  my $router_id;
  my $bytes_sent;
  my $bytes_recv;
  my $packets_sent;
  my $packets_recv;
  my $max_version;
  my $dtls_port; 

  my $insert_router_query   ="INSERT  router (pub_key,last_update_epoch)";
     $insert_router_query .=" VALUES ( ?, unix_timestamp(now()) )";
     $insert_router_query  .=" ON DUPLICATE KEY UPDATE last_update_epoch=unix_timestamp(now()) ";
  my $insert_router_sth= $dbh->prepare($insert_router_query) or die;
  my $select_router_id_query = "SELECT router_id from  router where pub_key=?";
  my $select_router_id_sth= $dbh->prepare($select_router_id_query) or die;
   


  #assume correct version!
  if($public_desc =~/^([0-9.]+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s([a-fA-F0-9]+),([a-fA-F0-9]+)/){
    # print("got it! $public_desc");
    $ip_addr=$1;
    $port=$2;
    $is_client=$3;
    $is_exit=$4;
    $kbps=$5;
    $pub_key=$6.",".$7;
 
    #temporary fix for sebastian's ip
    #if($ip_addr eq "107.18.47.78"){
    #  $ip_addr="78.47.18.107";
    #}
 
    #--only allow alterations when reported IP and real ip match. 
    #if(($ip_addr ne $ENV{REMOTE_ADDR}) && ("127.0.0.1" ne $ENV{REMOTE_ADDR} )){
    if(($ip_addr eq "0.0.0.0") && ("127.0.0.1" ne $ENV{REMOTE_ADDR} )){
      #return;
      $ip_addr=$ENV{REMOTE_ADDR};
    }   
 
    #insert into list of routers
    #$query  ="INSERT  router (pub_key,last_update_epoch)";
    #$query .=" VALUES ( ?, unix_timestamp(now()) )";
    #$query .=" ON DUPLICATE KEY UPDATE last_update_epoch=unix_timestamp(now()) ";
    #my $sth=$dbh->prepare($query); 
    $insert_router_sth->execute($pub_key) or die "cannot insert router query='$query'!\n";
    $temp=$ENV{REMOTE_ADDR};
    print("remote=$temp\n"); 
    #$sth->finish();  
    $insert_router_sth->finish();

    #get router_id;
    #$query  ="SELECT router_id from  router where pub_key=?";
    #my $sth2=$dbh->prepare($query);
    #$sth2->execute($pub_key) or die "execute router_id router query='$query'!\n";
    $select_router_id_sth->execute($pub_key) or die "execute router_id router query='$query'!\n";
    #my( $id, $name, $title, $phone );
    $select_router_id_sth->bind_columns( undef, \$router_id);
    if( $select_router_id_sth->fetch() )  {
        
    }
    else{
      print("no router_id found! $pub_key\n");
      return 0;
    }
    print("router_id=$router_id\n");
    #$sth2->finish(); 

    #insert into router updates;
    $query  ="INSERT IGNORE router_updates (router_id,epoch,ip_address,port,is_exit,bw_limit)";
    $query .=" VALUES ( $router_id, unix_timestamp(now()),inet_aton('".$ip_addr."' ),$port, $is_exit, $kbps )";
    my $sth3=$dbh->prepare($query);
    #print($query);
    $sth3->execute() or die "cannot insert router_update='$query'!\n";
    #$sth3->finish();
    
    return 0;
  } 
  if($public_desc =~/([0-9.]+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s([a-fA-F0-9]+),([a-fA-F0-9]+)/){
    $ip_addr=$1;
    $max_version=$2;
    $port=$3;
    $dtls_port=$4;
    $is_exit=$5;
    $kbps=$6;
    $packets_sent=$7;
    $bytes_sent=$8;
    $packets_recv=$9;
    $bytes_recv=$10;
    $pub_key=$11.",".$12;
   
    #print "got version2 packets_sent=$packets_sent packets_recv=$packets_recv \n"; 
       #--only allow alterations when reported IP and real ip match
    
    #if(($ip_addr ne $ENV{REMOTE_ADDR}) && ("127.0.0.1" ne $ENV{REMOTE_ADDR} )){
    if(($ip_addr eq "0.0.0.0") && ("127.0.0.1" ne $ENV{REMOTE_ADDR} )){
      #return;
      $ip_addr=$ENV{REMOTE_ADDR};
    }
    #do base insert
    $insert_router_sth->execute($pub_key) or die "cannot insert router query='$query'!\n";
    $temp=$ENV{REMOTE_ADDR};
    print("remote=$temp\n");
    #$sth->finish();  
    $insert_router_sth->finish();

    #get router_id
    $select_router_id_sth->execute($pub_key) or die "execute router_id router query='$query'!\n";
    #my( $id, $name, $title, $phone );
    $select_router_id_sth->bind_columns( undef, \$router_id);
    if( $select_router_id_sth->fetch() )  {

    }
    else{
      print("no router_id found! $pub_key\n");
      return 0;
    }
    print("router_id=$router_id\n");
   

 
    #insert into router updates;
    $query  ="INSERT IGNORE router_updates (router_id,epoch,ip_address,port,is_exit,bw_limit,max_version,packets_sent,packets_recv,bytes_sent,bytes_recv)";
    $query .=" VALUES ( $router_id, unix_timestamp(now()),inet_aton('".$ip_addr."' ),$port, $is_exit, $kbps,$max_version,$packets_sent,$packets_recv,$bytes_sent,$bytes_recv )";
    my $sth3=$dbh->prepare($query);
    #print($query);
    $sth3->execute() or die "cannot insert router_update='$query'!\n";
    #$sth3->finish();

 
    return 0; 
  }
  print("did not! $public_desc");
  die;
  return -1; 
  
}

sub read_network_list{
   my $version=shift;

   my  $ip_addr;
   my $port=$2;
   my  $is_client;
   my  $is_exit;
   my  $kbps;
   my  $pub_key;
   my $query;

   #get router_id;
   $query  ="select inet_ntoa(ip_address),port,0,is_exit,bw_limit, pub_key from router_updates,router where epoch>unix_timestamp(now())-1200 and router_updates.router_id=router.router_id and  last_update_epoch=epoch group by router.router_id order by ip_address;";
   my $sth2=$dbh->prepare($query);
   $sth2->execute() or die "execute router_id router query='$query'!\n";
   #my( $id, $name, $title, $phone );
   $sth2->bind_columns( undef, \$ip_addr,\$port,\$is_client,\$is_exit,\$kbps,\$pub_key);
   while( $sth2->fetch() )  {
      print("$ip_addr $port $is_client $is_exit $kbps $pub_key\n");
   }
   $sth2->finish();
  
 
}

sub main {
   my $cgi = new CGI;
   my $pub_key;
   my %user_input;
   my $com;  
   my $public_desc;
   my $temp;
   my $version;
   my $user_id;

   $config{'db_password'}="";
   load_config();

   if(not defined $config{'db_username'} || not defined $config{'db_name'}){
     return;
   }

   #initialize connection to db
  $dbh = DBI->connect("DBI:mysql:".$config{'db_name'},$config{'db_username'},$config{'db_password'});
  if(not defined ($dbh)){
       #internal_error();
       return;
  }


  #--- untaint variables
   if(defined($cgi->param('pub_key')) && $cgi->param('pub_key') =~/^([a-fA-F0-9,]{1,255})$/){
       $pub_key=$1;
   }


   if(defined($cgi->param('com')) && $cgi->param('com') =~/^(\w{1,20})$/){
       $user_input{"command"} = $1 ;
       $com =$1;        
   }
   if(defined($cgi->param('public_desc')) && $cgi->param('public_desc') =~/^([a-fA-F0-9 ,.]{1,1024})$/){

       $public_desc = $1;
   }
   if(defined($cgi->param('version')) && $cgi->param('version') =~/^([0-9 .]{1,14})$/){

       $version = $1;
   }


   print header();
              #-TYPE => 'text/xml');
   if (not (defined $version) or not (defined $com)){
      print("version AND command are required\n");
      return;
   }

   if("upload" eq $com){
      upload_server_descriptor($version,$public_desc);   
   }
   else { 
      if ("list" eq $com) {
         read_network_list($version);         
      }
   }


   #print header();
   #print ("hello_print");

   
   #print_user_rankings($user_id);

   #-warn("hello\n");
   $dbh->disconnect;
}

main();

